/* Copyright (c) 2022-2022, LiWangQian<liwangqian@huawei.com> All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this list of
 *    conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
 *    of conditions and the following disclaimer in the documentation and/or other materials
 *    provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
 *    to endorse or promote products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
#ifndef LOGPP_TIME_H
#define LOGPP_TIME_H

#include "logpp/configs.h"

#include <chrono>

LOGPP_NS_BEGIN

namespace time {

using clock = std::chrono::system_clock;
using seconds = std::chrono::seconds;
using milliseconds = std::chrono::milliseconds;
using microseconds = std::chrono::microseconds;
using nanoseconds = std::chrono::nanoseconds;
using timepoint = std::chrono::time_point<clock>;

static inline void fast_to_tm(const std::time_t& sec, struct tm* tm, int time_zone) noexcept
{
    constexpr int HOURS_IN_DAY = 24;
    constexpr int MINUTES_IN_HOUR = 60;
    constexpr int DAYS_FROM_UNIX_TIME = 2472632;
    constexpr int DAYS_FROM_YEAR = 153;
    constexpr int MAGIC1 = 146097;
    constexpr int MAGIC2 = 1461;
    tm->tm_sec = sec % MINUTES_IN_HOUR;
    int i = (sec / MINUTES_IN_HOUR);
    tm->tm_min = i % MINUTES_IN_HOUR;
    i /= MINUTES_IN_HOUR;
    tm->tm_hour = (i + time_zone) % HOURS_IN_DAY;
    tm->tm_mday = (i + time_zone) / HOURS_IN_DAY;
    int a = tm->tm_mday + DAYS_FROM_UNIX_TIME;
    int b = (a * 4 + 3) / MAGIC1;
    int c = (-b * MAGIC1) / 4 + a;
    int d = (c * 4 + 3) / MAGIC2;
    int e = (-d * MAGIC2) / 4 + c;
    int m = (5 * e + 2) / DAYS_FROM_YEAR;
    tm->tm_mday = -(DAYS_FROM_YEAR * m + 2) / 5 + e + 1;
    tm->tm_mon = (-m / 10) * 12 + m + 2;
    tm->tm_year = b * 100 + d - 6700 + (m / 10);
}

static inline auto current_zone()
{
    return 8; // "Asia/Beijing"
}

static inline void to_local_time(const timepoint &tp, std::tm *tm)
{
    fast_to_tm(time::clock::to_time_t(tp), tm, time::current_zone());
}

};

LOGPP_NS_END

#endif
